package biz.inspeed.test.thread1;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProCon2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ProCon2 pc = new ProCon2();
		BoundedBuffer buffer = pc.new BoundedBuffer();
		new Thread(pc.new Pro(buffer)).start();
		new Thread(pc.new Con(buffer)).start();
		new Thread(pc.new Con(buffer)).start();
		new Thread(pc.new Con(buffer)).start();
	}

	class Pro implements Runnable {
		BoundedBuffer buffer;

		public Pro(BoundedBuffer buffer) {
			this.buffer = buffer;
		}

		@Override
		public void run() {
			while (true) {
				try {
					buffer.put(UUID.randomUUID().toString());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	class Con implements Runnable {
		BoundedBuffer buffer;

		public Con(BoundedBuffer buffer) {
			this.buffer = buffer;
		}

		@Override
		public void run() {
			while(true){
				try {
					System.out.println(buffer.take());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	class BoundedBuffer {
		final Lock lock = new ReentrantLock();
		final Condition notEmpty = lock.newCondition();
		final Condition notFull = lock.newCondition();

		final int MAX_SIZE = 5;
		List<Object> list = new ArrayList<Object>();
		int count = 0;

		public void put(Object obj) throws InterruptedException {
			lock.lock();
			try {
				while (count == MAX_SIZE) {
					notFull.await();
				}
				list.add(obj);
				count++;
				System.out.println("call: notEmpty.signal()");
				notEmpty.signal();
			} finally {
				lock.unlock();
			}
		}

		public Object take() throws InterruptedException {
			lock.lock();
			try {
				while (count == 0) {
					notEmpty.await();
				}
				Object obj = list.remove(0);
				count --;
				System.out.println("call: notFull.signal()");
				notFull.signal();
				return obj;
			} finally {
				lock.unlock();
			}
		}
	}

}
